#pragma once

// 负责运行可执行文件
#include "../comm/comm.hpp"
#include "../comm/log.hpp"

using namespace Ns_Comm;

class Runner
{
private:
    static void RestrictResources(int cpu_limit, int mem_limit)
    {
        struct rlimit cpu_rlimit;
        cpu_rlimit.rlim_max = RLIM_INFINITY;
        cpu_rlimit.rlim_cur = cpu_limit;

        setrlimit(RLIMIT_CPU, &cpu_rlimit);

        struct rlimit mem_rlimit;
        mem_rlimit.rlim_max = RLIM_INFINITY;
        // cout << mem_limit << endl;
        mem_rlimit.rlim_cur = mem_limit * 1024;
        setrlimit(RLIMIT_AS, &mem_rlimit);
    }

public:
    Runner() {}
    // cpu_limit：限制CPU资源，也就是运行时间，单位：秒
    // mem_limit：限制虚拟内存空间，也就是内存占用大小，单位：KB
    static int ExcutedFile(const std::string &filename, int cpu_limit, int mem_limit)
    {
        /********************************************************
         * 程序运行结果：
         * 1. 运行完毕，结果不对 对应标准错误
         * 2. 运行完毕，结果对  对应标准输出
         * 3. 运行异常          对应标准错误
         *
         *
         *  返回值 > 0：程序出现异常
         *  返回值 < 0：执行服务代码内部出现错误
         *  返回值 == 0：正常运行完毕
         *
         *
         *  防止恶意代码，需要约束代码使用的空间和时间
         *  返回信号时不仅是返回错误信息，还得返回信号对应的解释
         * *****************************************************/
        umask(0);
        int _stdin_fd = FileTool::Open(PathTool::SplicingExeStdin(filename), O_CREAT | O_RDONLY, 0644);
        int _stdout_fd = FileTool::Open(PathTool::SplicingExeStdout(filename), O_CREAT | O_TRUNC | O_WRONLY, 0644);
        int _stderr_fd = FileTool::Open(PathTool::SplicingExeStderr(filename), O_CREAT | O_TRUNC | O_WRONLY, 0644);

        if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
        {
            LOG(LogError, "%s中第 %d 行打开文件错误\n", __FILE__, __LINE__);
            return OpenFile_Err;
        }


        int id = fork();
        if (id < 0)
        {
            LOG(LogError, "%s中第 %d 行创建子进程错误\n", __FILE__, __LINE__);
            return Fork_Err;
        }
        else if (id == 0)
        {
            // 子进程
            FileTool::Redirect(0, _stdin_fd);
            FileTool::Redirect(1, _stdout_fd);
            FileTool::Redirect(2, _stderr_fd);

            RestrictResources(cpu_limit, mem_limit);

            execl(PathTool::SplicingExe(filename).c_str(), PathTool::SplicingExe(filename).c_str(), nullptr);

            exit(0);
        }
        else
        {
            FileTool::Close(_stdin_fd);
            FileTool::Close(_stdout_fd);
            FileTool::Close(_stderr_fd);
            int status = 0;
            waitpid(id, &status, 0);
            LOG(LogInfo, "运行完毕，info: %d\n", WTERMSIG(status));
            return WTERMSIG(status);
        }
    }
    ~Runner() {}
};